#include "los_hw.h"
#include "linux/interrupt.h"
#include "linux/kernel.h"
#include "dmac_ext.h"
#include "hi_dmac.h"
#include "los_event.h"
#include "hisoc/dmac.h"
#include "asm/delay.h"
#include "linux/module.h"
#include "linux/device.h"
#include "linux/platform_device.h"

void *reg_dmac_base_va;

extern dmac_peripheral  g_peripheral[DMAC_MAX_PERIPHERALS];

extern void dma_cache_clean(AARCHPTR start, AARCHPTR end);
extern void dma_cache_inv(AARCHPTR start, AARCHPTR end);

#define RX  0
#define TX  1

static int dmac_chan[CHANNEL_NUM] = {
#if CHANNEL_NUM >= 1
    0,
#endif

#if CHANNEL_NUM >= 2
    1,
#endif

#if CHANNEL_NUM >= 3
    2,
#endif

#if CHANNEL_NUM >= 4
    3,
#endif
};

#define CLR_INT(i) ((*(uint32_t *)(DMAC_REG_BASE+0x008)) = (1<<i))

int g_chan_status[CHANNEL_NUM];

#define     DMAC_EVENTT_TYPE       0x01
EVENT_CB_S  dmac_event[CHANNEL_NUM];

static dmac_lli *pheadlliList[CHANNEL_NUM]  = {

#if CHANNEL_NUM >= 1
    NULL,
#endif

#if CHANNEL_NUM >= 2
    NULL,
#endif

#if CHANNEL_NUM >= 3
    NULL,
#endif

#if CHANNEL_NUM >= 4
    NULL,
#endif
};


static dmac_lli_bak *plliList_bak[CHANNEL_NUM] = {

#if CHANNEL_NUM >= 1
    NULL,
#endif

#if CHANNEL_NUM >= 2
    NULL,
#endif

#if CHANNEL_NUM >= 3
    NULL,
#endif

#if CHANNEL_NUM >= 4
    NULL,
#endif
};
#ifdef DMA_DEBUG
#define dma_debug printk
#else
#define dma_debug(fmt, ...) do {} while (0);
#endif
#define dma_err printk

mem_addr mem_num[MEM_MAX_NUM] = {
    {DDRAM_ADRS, DDRAM_SIZE}
    //{FLASH_BASE, FLASH_SIZE}
};

typedef void REG_ISR(int dma_chn, int dma_status) ;
REG_ISR *isr_function[CHANNEL_NUM];

/*funciton declaration*/

static void dmac_channel_close(unsigned int channel, unsigned int channel_status);

static int mem_check(unsigned int addr)
{
    unsigned int cnt;

    for (cnt = 0; cnt < MEM_MAX_NUM; cnt++) {
        if ((addr >= mem_num[cnt].memaddr_base) &&
                (addr <= (mem_num[cnt].memaddr_base + mem_num[cnt].memsize))) {
            return 0;
        }
    }

    return -1;
}

irqreturn_t dmac_isr(int irq, void *dev_id)
{
    unsigned int channel_status;
    unsigned int channel_tc_status, channel_err_status;
    unsigned int i;

    dma_debug("isr entry\n");

    hidmac_readw(DMAC_INTSTATUS, channel_status);

    /* decide which channel has trigger the interrupt */
    for (i = 0; i < DMAC_MAX_CHANNELS; i++) {
        if ((channel_status >> i) & 0x01) {

            hidmac_readw(DMAC_INTTCSTATUS, channel_tc_status);
            hidmac_readw(DMAC_INTERRORSTATUS, channel_err_status);

            if ((g_chan_status[i] == DMAC_CHN_VACANCY)
                    && ((isr_function[i]) == NULL)) {
                if (0x1 == ((channel_tc_status >> i) & 0x1)) {
                    hidmac_writew(DMAC_INTTCCLEAR, (0x1 << i));
                } else if (0x1 == ((channel_err_status >> i) & 0x1)) {
                    hidmac_writew(DMAC_INTERRCLR, (0x1 << i));
                }
                continue;
            }

            /*
             * save the current channel transfer
             * status to g_chan_status[i]
             */
            if (0x1 == ((channel_tc_status >> i) & 0x1)) {
                g_chan_status[i] = DMAC_CHN_SUCCESS;
                hidmac_writew(DMAC_INTTCCLEAR, (0x1 << i));
            } else if (0x1 == ((channel_err_status >> i) & 0x1)) {
                g_chan_status[i] = -DMAC_CHN_ERROR;
                hidmac_writew(DMAC_INTERRCLR, (0x01 << i));
            } else
                printk(KERN_ERR "Isr Error in DMAC_IntHandeler"\
                       "%d! channel\n", i);

            if ((isr_function[i]) != NULL) {
                isr_function[i](i, g_chan_status[i]);
                LOS_EventWrite(&dmac_event[i], DMAC_EVENTT_TYPE);
            }
        }
    }

    return IRQ_HANDLED;
}

/*
 * update the state of channels
 */
static int dma_update_status(unsigned int channel)
{
    unsigned int channel_status;
    unsigned int channel_tc_status[3];
    unsigned int channel_err_status[3];
    unsigned int i = channel, j;
    unsigned int timeout_tick = 0;

    timeout_tick = LOS_TickCountGet() + LOS_MS2Tick(5000); //5000 ms
    if (g_chan_status[i]) {
        return g_chan_status[i];
    }

    while (1) {
        for (j = 0; j < 3; j++) {
            hidmac_readw(DMAC_INTTCSTATUS, channel_status);
            channel_tc_status[j] = (channel_status >> i) & 0x1;
            hidmac_readw(DMAC_INTERRORSTATUS, channel_status);
            channel_err_status[j] = (channel_status >> i) & 0x1;
        }

        if ((channel_tc_status[0] == 0x1)
                && (channel_tc_status[1] == 0x1)
                && (channel_tc_status[2] == 0x1)) {
            g_chan_status[i] = DMAC_CHN_SUCCESS;
            hidmac_writew(DMAC_INTTCCLEAR, (0x1 << i));
            break;
        }

        if ((channel_err_status[0] == 0x1)
                && (channel_err_status[1] == 0x1)
                && (channel_err_status[2] == 0x1)) {
            g_chan_status[i] = -DMAC_CHN_ERROR;
            dma_err("Error in DMAC %d finish!\n", i);
            hidmac_writew(DMAC_INTERRCLR, (0x01 << i));
            break;
        }
        if (LOS_TickCountGet() > timeout_tick) {
            dma_err("DMA Timeout!\n");
            g_chan_status[i] = -DMAC_CHN_TIMEOUT;
            break;
        }
    }

    return g_chan_status[i];
}


/*
 * check the state of channels
 */
static int dmac_check_over(unsigned int channel)
{
    int ret = 0;

    if (-DMAC_CHN_ERROR == g_chan_status[channel]) {
        dma_err("Error transfer %d finished\n", channel);
        hidmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE);
        g_chan_status[channel] = DMAC_CHN_VACANCY;
        ret = -DMAC_CHN_ERROR;
    } else if (DMAC_NOT_FINISHED == g_chan_status[channel]) {
        ret = DMAC_NOT_FINISHED;
    } else if (DMAC_CHN_ALLOCAT == g_chan_status[channel]) {
        ret = DMAC_CHN_ALLOCAT;
    } else if (DMAC_CHN_VACANCY == g_chan_status[channel]) {
        ret = DMAC_CHN_VACANCY;
    } else if (-DMAC_CHN_TIMEOUT == g_chan_status[channel]) {
        dma_err("transfer %d timeout!\n", channel);
        ret = -DMAC_CHN_TIMEOUT;
    } else if (DMAC_CHN_SUCCESS == g_chan_status[channel])
        /*The transfer of Channel %d has finished successfully!*/
    {
        ret = DMAC_CHN_SUCCESS;
    } else {
        hidmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE);
        g_chan_status[channel] = DMAC_CHN_VACANCY;
        ret = -DMAC_CHN_ERROR;
    }
    return ret;
}


/*
 * allocate channel.
 */
unsigned int dmac_channel_allocate(void *pisr)
{
    unsigned int i = 0, chaninfo;

    for (i = 0; i < CHANNEL_NUM; i++) {
        dmac_check_over(dmac_chan[i]);
    }

    hidmac_readw(DMAC_ENBLDCHNS, chaninfo);
    chaninfo = chaninfo & 0x00ff;

    for (i = 0; i < CHANNEL_NUM; i++) {
        if (g_chan_status[dmac_chan[i]] == DMAC_CHN_VACANCY) {
            chaninfo = chaninfo >> dmac_chan[i];
            /* channel is disabled currently */
            if (0x00 == (chaninfo & 0x01)) {
                /*clear the interrupt in this channel */
                hidmac_writew(DMAC_INTERRCLR,
                              (0x01 << dmac_chan[i]));
                hidmac_writew(DMAC_INTTCCLEAR,
                              (0x01 << dmac_chan[i]));

                g_chan_status[dmac_chan[i]]
                    = DMAC_CHN_ALLOCAT;
                return dmac_chan[i];
            }
        }
    }
    dma_err("[%s:%d]no to alloc\n", __func__, __LINE__);
    return DMAC_CHANNEL_INVALID;
}


int dmac_register_isr(unsigned int ch, void *pisr)
{
    if (ch >= CHANNEL_NUM) {
        dma_err("invalid dma channel %d !\n", ch);
        return -1;
    }

    /*clear the interrupt in this channel */
    hidmac_writew(DMAC_INTERRCLR, (0x01 << ch));
    hidmac_writew(DMAC_INTTCCLEAR, (0x01 << ch));

    isr_function[ch] = (void *)pisr;
    g_chan_status[ch] = DMAC_CHN_ALLOCAT;

    return 0;
}

/*
 * close channel
 */
static void dmac_channel_close(unsigned int channel, unsigned int channel_status)
{
    if (pheadlliList[channel]) {
        dma_debug("free: 0x%x\n", pheadlliList[channel]);
        free(pheadlliList[channel]);
    }
    pheadlliList[channel] = NULL;
    isr_function[channel] = NULL;
    g_chan_status[channel] = DMAC_CHN_VACANCY;
    hidmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE);
}

/*
 * free channel
 */
int dmac_channel_free(unsigned int channel)
{
    if (pheadlliList[channel]) {
        free(pheadlliList[channel]);
    }
    pheadlliList[channel] = NULL;

    isr_function[channel] = NULL;
    g_chan_status[channel] = DMAC_CHN_VACANCY;
    hidmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE);
    return 0;
}
static int dmac_check_request(unsigned int peripheral_addr,
                              int direction)
{
    int i = 0;
    /* check request pipe with peripheral_addr */
    for (i = direction; i < DMAC_MAX_PERIPHERALS; i = i + 2) {
        if (g_peripheral[i].peri_addr == peripheral_addr) {
            return i;
        }
    }

    dma_err("Invalid devaddr\n");

    return -1;
}

unsigned int dmac_get_current_dest(unsigned int channel)
{
    unsigned int reg_value = 0;
    hidmac_readw(DMAC_CxDESTADDR(channel), reg_value);
    return reg_value;
}

/*
 * alloc_dma_lli_space
 * output:
 */
int allocate_dmalli_space(dmac_lli **ppheadlli, unsigned int length)
{
    unsigned int *alloc_addr = NULL;
    unsigned int num_lli = 0;
    unsigned int alloc_length = 0;

    num_lli = length / (DMAC_TRANS_SIZE);

    if ((length % (DMAC_TRANS_SIZE)) != 0) {
        num_lli++;
    }
    alloc_length = num_lli * sizeof(dmac_lli);
    alloc_length = ALIGN(alloc_length, CACHE_ALIGNED_SIZE);
    alloc_addr = (unsigned int *)memalign(CACHE_ALIGNED_SIZE, alloc_length);
    if (!alloc_addr) {
        dma_err("can't malloc llimem for dma!\n ");
        return -1;
    }
    dma_debug("num_lli :%d, alloc_length:%d", num_lli, alloc_length);
    memset_s(alloc_addr, alloc_length, 0, alloc_length);
    *ppheadlli = (dmac_lli *)alloc_addr;
    dma_debug("dma malloc mem %p,num_lli:%d,length:%d, ppheadlli:%p\n", alloc_addr, lli_num, alloc_length, *ppheadlli);
    return alloc_length;
}

void free_dmalli_space(dmac_lli *ppheadlli)
{
    if (ppheadlli) {
        free(ppheadlli);
        ppheadlli = NULL;
    }
}


/*
 * config register for memory to memory DMA tranfer without LLI
 * note:
 * the channel is not actually started yet, you need to call
 * dmac_channelstart to start it up.
 */
int dmac_start_m2m(unsigned int channel, unsigned int psource,
                   unsigned int pdest, unsigned int uwnumtransfers)
{
    unsigned int uwchannel_num, tmp_trasnsfer;

    if (mem_check(psource) | mem_check(pdest)) {
        dma_err("Invalid dma addr psrc = 0x%x, pdest = 0x%x!\n",
                psource, pdest);
        return -EINVAL;
    }

    if (uwnumtransfers > (DMAC_MAXTRANSFERSIZE << 2)) {
        dma_err("Invalidate transfer size,size=%x\n",
                uwnumtransfers);
        return -EINVAL;
    }

    uwchannel_num = channel;

    if ((uwchannel_num == DMAC_CHANNEL_INVALID)
            || (uwchannel_num > CHANNEL_NUM)) {
        dma_err("invalid dma channel %d\n", uwchannel_num);
        return -EFAULT;
    }

    hidmac_writew(DMAC_CxSRCADDR(uwchannel_num), psource);
    hidmac_writew(DMAC_CxDESTADDR(uwchannel_num), pdest);
    hidmac_writew(DMAC_CxLLI(uwchannel_num), 0);
    tmp_trasnsfer = (uwnumtransfers) & 0xfff;
    tmp_trasnsfer = tmp_trasnsfer | (DMAC_CxCONTROL_M2M & (~0xfff));
    hidmac_writew(DMAC_CxCONTROL(uwchannel_num), tmp_trasnsfer);
    hidmac_writew(DMAC_CxCONFIG(uwchannel_num), DMAC_CxCONFIG_M2M);
    return 0;
}


/*
 * channel enable
 * start a dma transfer immediately
 */
int dmac_channelstart(unsigned int u32channel)
{
    unsigned int reg_value = 0;

    if (u32channel >= DMAC_MAX_CHANNELS) {
        dma_err("channel larger %d\n", DMAC_MAX_CHANNELS);
        return -EINVAL;
    }

    g_chan_status[u32channel] = DMAC_NOT_FINISHED;
    hidmac_readw(DMAC_CxCONFIG(u32channel), reg_value);
    reg_value &= 0x0007ffff;
    hidmac_writew(DMAC_CxCONFIG(u32channel),
                  (reg_value | DMAC_CHANNEL_ENABLE));

    return 0;
}

/*
 * wait for transfer end
 */
int dmac_wait(unsigned int channel)
{
    int result = 0;

    if (channel >= CHANNEL_NUM) {
        return -1;
    }

    while (1) {
        result = dma_update_status(channel);
        if (result == -DMAC_CHN_ERROR) {
            dma_err("Transfer Error.\n");
            return -1;
        } else  if (result == DMAC_NOT_FINISHED) {
            udelay(10);
        } else if (result == DMAC_CHN_SUCCESS) {
            return DMAC_CHN_SUCCESS;
        } else if (result == DMAC_CHN_VACANCY) {
            return DMAC_CHN_SUCCESS;
        } else if (result == -DMAC_CHN_TIMEOUT) {
            dma_err("Timeout.\n");
            hidmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE);
            g_chan_status[channel] = DMAC_CHN_VACANCY;
            return -1;
        }
    }
}


/*
 * buile LLI for memory to memory DMA tranfer
 */
int dmac_buildllim2m(dmac_lli *ppheadlli,
                     unsigned int psource, unsigned int pdest,
                     unsigned int totaltransfersize, unsigned int numtransfers)
{
    unsigned int num_lli = 0;
    unsigned int lli_last = 0;
    unsigned int srcaddr, destaddr;
    dmac_lli *pheadlli = NULL;
    unsigned int j;

    if (mem_check(psource) | mem_check(pdest)) {
        dma_err("Invalid dma addr psrc = 0x%x, pdest = 0x%x!\n",
                psource, pdest);
        return -EINVAL;
    }

    if (ppheadlli == NULL) {
        dma_err("ppheadlli is NULL!\n");
        return -ENOMEM;
    }

    pheadlli = ppheadlli;

    num_lli = (totaltransfersize / numtransfers);
    if ((totaltransfersize % numtransfers) != 0) {
        lli_last = 1, ++num_lli;
    }

    for (j = 0; j < num_lli; j++) {
        srcaddr = (psource + (j * numtransfers));
        hidmac_writew(&(pheadlli->src_addr), srcaddr);
        destaddr = (pdest + (j * numtransfers));
        hidmac_writew(&(pheadlli->dst_addr), destaddr);
        if (j == (num_lli - 1)) {
            hidmac_writew(&(pheadlli->next_lli), 0);
        } else {
            hidmac_writew(&(pheadlli->next_lli), (((unsigned int)(pheadlli + 1) & (~0x3)) | DMAC_CxLLI_LM));
        }

        //control, 1 trans_size = 4Bytes(32bit mode)
        if ((j == (num_lli - 1)) && (lli_last == 0)) {
            hidmac_writew(&(pheadlli->lli_transfer_ctrl),
                          ((DMAC_CxCONTROL_LLIM2M & (~0xfff))
                           | (numtransfers >> 2)
                           | DMAC_CxCONTROL_INT_EN));

        } else if ((j == (num_lli - 1)) && (lli_last == 1)) {
            hidmac_writew(&(pheadlli->lli_transfer_ctrl),
                          ((DMAC_CxCONTROL_LLIM2M & (~0xfff))
                           | ((totaltransfersize % numtransfers) >> 2)
                           | DMAC_CxCONTROL_INT_EN));
        } else
            hidmac_writew(&(pheadlli->lli_transfer_ctrl),
                          (((DMAC_CxCONTROL_LLIM2M & (~0xfff)) | (numtransfers >> 2))
                           & 0x7fffffff));

        pheadlli++;
    }

    return 0;
}

/*
 * buile LLI for peripheral to memory DMA tranfer
 */
int dmac_buildllip2m(dmac_lli *ppheadlli,
                     unsigned int peripheralid, unsigned int pdest,
                     unsigned int totaltransfersize, unsigned int numtransfers)
{
    unsigned int num_lli = 0;
    unsigned int lli_last = 0;
    unsigned int srcaddr, destaddr;
    dmac_lli *pheadlli = NULL;
    unsigned int j = 0;

    if (mem_check(pdest)) {
        dma_err("Invalid dma addr ,pdest = 0x%x!\n", pdest);
        return -EINVAL;
    }
    if (peripheralid >= DMAC_MAX_PERIPHERALS) {
        dma_err("Invalid peripheralid,id= %d!\n", pdest);
        return -EINVAL;
    }
    if (ppheadlli == NULL) {
        dma_err("ppheadlli is NULL!\n");
        return -ENOMEM;
    }

    pheadlli = ppheadlli;

    num_lli = (totaltransfersize / numtransfers);
    if ((totaltransfersize % numtransfers) != 0) {
        lli_last = 1, ++num_lli;
    }

    //building  lli
    for (j = 0; j < num_lli; j++) {
        srcaddr = g_peripheral[peripheralid].peri_addr;                                //(psource + (j*numtransfers));
        hidmac_writew(&(pheadlli->src_addr), srcaddr);
        destaddr = (pdest + (j * numtransfers));
        hidmac_writew(&(pheadlli->dst_addr), destaddr);
        if (j == (num_lli - 1)) {
            hidmac_writew(&(pheadlli->next_lli), (((unsigned int)ppheadlli & (~0x3)) | DMAC_CxLLI_LM));
        } else {
            hidmac_writew(&(pheadlli->next_lli),
                          (((unsigned int)(pheadlli + 1) & (~0x3)) | DMAC_CxLLI_LM));
        }
        //control, 1 trans_size = 1Byte(8bit mode)
        if ((j == (num_lli - 1)) && (lli_last == 0)) {
            hidmac_writew(&(pheadlli->lli_transfer_ctrl),
                          ((g_peripheral[peripheralid].transfer_ctrl & (~0xfff))
                           | (numtransfers & 0xfff)));

        } else if ((j == (num_lli - 1)) && (lli_last == 1)) {
            hidmac_writew(&(pheadlli->lli_transfer_ctrl),
                          ((g_peripheral[peripheralid].transfer_ctrl & (~0xfff))
                           | ((totaltransfersize % (numtransfers & 0xfff)))));
        } else
            hidmac_writew(&(pheadlli->lli_transfer_ctrl),
                          (((g_peripheral[peripheralid].transfer_ctrl
                             & (~0xfff)) | (numtransfers & 0xfff))
                           & 0x7fffffff));

        pheadlli++;
    }

    return 0;
}

/*
 * disable channel
 * used before the operation of register configuration
 */
int dmac_channelclose(unsigned int chan)
{
    unsigned int reg, count;

    if (chan >= DMAC_MAX_CHANNELS) {
        dma_err("chan larger than total.\n");
        return -EINVAL;
    }

    hidmac_readw(DMAC_CxCONFIG(chan), reg);

#define CHANNEL_CLOSE_IMMEDIATE
#ifdef CHANNEL_CLOSE_IMMEDIATE
    reg &= 0xFFFFFFFE;
    hidmac_writew(DMAC_CxCONFIG(chan), reg);
#else
    reg |= DMAC_CONFIGURATIONx_HALT_DMA_ENABLE;
    /*ignore incoming dma request*/
    hidmac_writew(DMAC_CxCONFIG(chan), reg);
    hidmac_readw(DMAC_CxCONFIG(chan), reg);
    /*if FIFO is empty*/
    while ((reg & DMAC_CONFIGURATIONx_ACTIVE)
            == DMAC_CONFIGURATIONx_ACTIVE) {
        hidmac_readw(DMAC_CxCONFIG(chan), reg);
    }
    reg &= 0xFFFFFFFE;
    hidmac_writew(DMAC_CxCONFIG(chan), reg);
#endif

    hidmac_readw(DMAC_ENBLDCHNS, reg);
    reg = reg & 0x00ff;
    count = 0;
    while (((reg >> chan) & 0x1) == 1) {
        hidmac_readw(DMAC_ENBLDCHNS, reg);
        reg = reg & 0x00ff;
        if (count++ > 10000) {
            dma_err("close failure.\n");
            return -1;
        }
    }
    g_chan_status[chan] = DMAC_CHN_VACANCY;
    return 0;
}


/*
 *    load configuration from LLI for peripheral to memory
 */
int dmac_start_llip2m(unsigned int channel, dmac_lli *pfirst_lli, unsigned int peri_id)
{
    unsigned int uwchan_num = 0;
    dmac_lli  *plli;
    if (NULL == pfirst_lli) {
        dma_err("Invalidate LLI head!\n");
        return -EFAULT;
    }

    uwchan_num = channel;
    plli = pfirst_lli;
    if ((uwchan_num == DMAC_CHANNEL_INVALID)
            || (uwchan_num > CHANNEL_NUM)) {
        dma_err("invalid dma channel %d\n", uwchan_num);
        return -EINVAL;
    }
    hidmac_writew(DMAC_CxCONFIG(uwchan_num), DMAC_CxDISABLE);
    hidmac_writew(DMAC_CxSRCADDR(uwchan_num),
                  (unsigned int)(plli->src_addr));
    hidmac_writew(DMAC_CxDESTADDR(uwchan_num),
                  (unsigned int)(plli->dst_addr));
    hidmac_writew(DMAC_CxLLI(uwchan_num),
                  (unsigned int)(plli->next_lli));
    hidmac_writew(DMAC_CxCONTROL(uwchan_num),
                  (unsigned int)(plli->lli_transfer_ctrl));
    hidmac_writew(DMAC_CxCONFIG(uwchan_num), g_peripheral[peri_id].transfer_cfg);

    return 0;
}


/*
 *    load configuration from LLI for memory to memory
 */
int dmac_start_llim2m(unsigned int channel, dmac_lli *pfirst_lli)
{
    unsigned int uwchan_num;
    dmac_lli  *plli;
    unsigned int first_lli;
    if (NULL == pfirst_lli) {
        dma_err("Invalidate LLI head!\n");
        return -EFAULT;
    }

    uwchan_num = channel;
    plli = pfirst_lli;
    if ((uwchan_num == DMAC_CHANNEL_INVALID)
            || (uwchan_num > CHANNEL_NUM)) {
        dma_err("invalid dma channel %d\n", uwchan_num);
        return -EINVAL;
    }

    hidmac_writew(DMAC_CxCONFIG(uwchan_num), DMAC_CxDISABLE);
    hidmac_writew(DMAC_CxSRCADDR(uwchan_num),
                  (unsigned int)(plli->src_addr));
    hidmac_writew(DMAC_CxDESTADDR(uwchan_num),
                  (unsigned int)(plli->dst_addr));
    hidmac_writew(DMAC_CxLLI(uwchan_num),
                  (unsigned int)(plli->next_lli));
    hidmac_writew(DMAC_CxCONTROL(uwchan_num),
                  (unsigned int)(plli->lli_transfer_ctrl));
    hidmac_writew(DMAC_CxCONFIG(uwchan_num), DMAC_CxCONFIG_LLIM2M);

    return 0;
}


/*
 * load configuration from LLI for memory and peripheral
 */
int dmac_start_llim2p(unsigned int channel,
                      unsigned int *pfirst_lli,
                      unsigned int uwperipheralid)
{
    unsigned int uwchan_num = 0;
    dmac_lli plli = {0};
    unsigned int first_lli = 0;
    unsigned int tmp = 0;

    if (NULL == pfirst_lli) {
        dma_err("Invalid LLI head!\n");
        return -EINVAL;
    }

    uwchan_num = channel;
    if ((uwchan_num == DMAC_CHANNEL_INVALID)
            || (uwchan_num > CHANNEL_NUM)) {
        dma_err("invalid dma channel %d\n", uwchan_num);
        return -EINVAL;
    }

    memset_s(&plli, sizeof(plli), 0, sizeof(plli));
    first_lli = (unsigned int)pfirst_lli[1];
    hidmac_readw(first_lli, plli.src_addr);
    hidmac_readw(first_lli + 4, plli.dst_addr);
    hidmac_readw(first_lli + 8, plli.next_lli);
    hidmac_readw(first_lli + 12, plli.lli_transfer_ctrl);

    dmac_channelclose(uwchan_num);
    hidmac_writew(DMAC_INTTCCLEAR, (0x1 << uwchan_num));
    hidmac_writew(DMAC_INTERRCLR, (0x1 << uwchan_num));
    hidmac_writew(DMAC_SYNC, 0x0);

    hidmac_readw(DMAC_CxCONFIG(uwchan_num), tmp);
    hidmac_writew(DMAC_CxCONFIG(uwchan_num), tmp | DMAC_CxDISABLE);
    hidmac_writew(DMAC_CxSRCADDR(uwchan_num), plli.src_addr);
    hidmac_writew(DMAC_CxDESTADDR(uwchan_num), plli.dst_addr);
    hidmac_writew(DMAC_CxLLI(uwchan_num), plli.next_lli);
    hidmac_writew(DMAC_CxCONTROL(uwchan_num), plli.lli_transfer_ctrl);

    return 0;
}


/*
 * enable memory and peripheral dma transfer
 * note:
 *  it is necessary to call dmac_channelstart to enable channel
 */
int dmac_start_m2p(unsigned int channel, unsigned int memaddr,
                   unsigned int uwperipheralid, unsigned int uwnumtransfers,
                   unsigned int next_lli_addr)
{
    unsigned int uwtrans_control = 0;
    unsigned int tmp;
    unsigned int uwdst_addr = 0;
    unsigned int uwsrc_addr = 0;
    unsigned int uwwidth;
    unsigned int uwchan_num;

    if (mem_check(memaddr)) {
        dma_err("Invalid dma addr memaddr = 0x%x!\n", memaddr);
        return -EINVAL;
    }

    if ((uwperipheralid > 15)) {
        dma_err("Invalid peripheral id%x\n", uwperipheralid);
        return -EINVAL;
    }

    uwchan_num = channel;
    if ((uwchan_num == DMAC_CHANNEL_INVALID)
            || (uwchan_num > CHANNEL_NUM)) {
        dma_err("invalid channel %d\n", uwchan_num);
        return -EFAULT;
    }

    /* must modified with different peripheral */
    uwwidth = g_peripheral[uwperipheralid].transfer_width;

    /*
     * check transfer direction
     * even number-->TX, odd number-->RX
     */
    uwsrc_addr = memaddr;
    uwdst_addr = (unsigned int)(g_peripheral[uwperipheralid].peri_addr);

    tmp = uwnumtransfers >> uwwidth;
    if (tmp & (~0x0fff)) {
        dma_err("Invalidate size%x\n", uwnumtransfers);
        return -EINVAL;
    }

    tmp = tmp & 0xfff;
    uwtrans_control = tmp | (g_peripheral[uwperipheralid].transfer_ctrl & (~0xfff));
    dma_err("(in)uwdst_addr = %#x\n", (unsigned int)uwdst_addr);
    hidmac_writew(DMAC_INTTCCLEAR, (0x1 << uwchan_num));
    hidmac_writew(DMAC_INTERRCLR, (0x1 << uwchan_num));
    hidmac_writew(DMAC_CxSRCADDR(uwchan_num), (unsigned int)uwsrc_addr);
    hidmac_writew(DMAC_CxDESTADDR(uwchan_num), (unsigned int)uwdst_addr);
    hidmac_writew(DMAC_CxCONTROL(uwchan_num), (unsigned int)uwtrans_control);
    hidmac_writew(DMAC_CxCONFIG(uwchan_num), (g_peripheral[uwperipheralid].transfer_cfg));

    return 0;
}

/*
 * enable memory and peripheral dma transfer
 * note:
 * it is necessary to call dmac_channelstart to enable channel
 */
int dmac_start_p2m(unsigned int channel,
                   unsigned int memaddr, unsigned int uwperipheralid,
                   unsigned int uwnumtransfers, unsigned int next_lli_addr)
{
    unsigned int uwtrans_control = 0;
    unsigned int tmp;
    unsigned int uwdst_addr = 0;
    unsigned int uwsrc_addr = 0;
    unsigned int uwwidth;
    unsigned int uwchan_num = 0;

    if (mem_check(memaddr)) {
        dma_err("Invalidate dma addr memaddr = 0x%x!\n", memaddr);
        return -EINVAL;
    }

    if ((uwperipheralid > 15)) {
        dma_err("Invalid peripheral id%x\n", uwperipheralid);
        return -EINVAL;
    }

    uwchan_num = channel;
    if ((uwchan_num == DMAC_CHANNEL_INVALID)
            || (uwchan_num > CHANNEL_NUM)) {
        dma_err("failure alloc\n");
        return -EFAULT;
    }

    /* must modified with different peripheral */
    uwwidth = g_peripheral[uwperipheralid].transfer_width;

    /*
     * check transfer direction
     * even number-->TX, odd number-->RX
     */
    uwsrc_addr = (unsigned int)(g_peripheral[uwperipheralid].peri_addr);
    uwdst_addr = memaddr;

    tmp = uwnumtransfers >> uwwidth;
    if (tmp & (~0x0fff)) {
        dma_err("Invalidate size%x\n", uwnumtransfers);
        return -EINVAL;
    }

    tmp = tmp & 0xfff;
    uwtrans_control = tmp | (g_peripheral[uwperipheralid].transfer_ctrl & (~0xfff));
    dma_debug("(in)uwdst_addr = %#x\n", (unsigned int)uwdst_addr);
    hidmac_writew(DMAC_INTTCCLEAR, (0x1 << uwchan_num));
    hidmac_writew(DMAC_INTERRCLR, (0x1 << uwchan_num));
    hidmac_writew(DMAC_CxSRCADDR(uwchan_num), (unsigned int)uwsrc_addr);
    hidmac_writew(DMAC_CxDESTADDR(uwchan_num), (unsigned int)uwdst_addr);
    hidmac_writew(DMAC_CxCONTROL(uwchan_num), (unsigned int)uwtrans_control);
    hidmac_writew(DMAC_CxCONFIG(uwchan_num), (g_peripheral[uwperipheralid].transfer_cfg));

    return 0;
}


/*
 * execute memory to memory dma transfer with LLI
 */
int dmac_llim2m_transfer(unsigned int source,
                         unsigned int dest,
                         unsigned int length)
{
    int retval = 0;
    int lli_length = 0;
    unsigned int chnn;
    dmac_lli *pllihead = NULL;

    if (mem_check(source) | mem_check(dest)) {
        dma_err("Invalid dma addr psrc = 0x%x, pdest = 0x%x!\n",
                source, dest);
        return -EINVAL;
    }

    //if src addr or dst addr no aligned by CACHE_ALIGNED_SIZE
    //then copy by cpu.
    if ((source & (CACHE_ALIGNED_SIZE - 1)) || (dest & (CACHE_ALIGNED_SIZE - 1)) || \
            (length & (CACHE_ALIGNED_SIZE - 1))) {
        memcpy_s((void *)dest, length, (void *)source, length);
        dma_err("dma addr is not aligned by CACHE_ALIGNED_SIZE,coping by cpu !\n");
        return 0;
    }
    //alloc dma channel
    chnn = dmac_channel_allocate(NULL);
    if (DMAC_CHANNEL_INVALID == chnn) {
        dma_err("no channel available, coping by cpu !\n");
        memcpy_s((void *)dest, length, (void *)source, length);
        return 0;
    }
    dma_debug("chnn:%d,src:0x%X,dst:0x%X,len:%d\n", chnn, source, dest, length);

    lli_length = allocate_dmalli_space(&pllihead, length);
    if (lli_length < 0) {
        return -1;
    }

    dma_cache_clean(source, source + length);
    dma_cache_inv(dest, dest + length);

    /*save head*/
    pheadlliList[chnn] = pllihead;

    dma_debug("pllihead: 0x%X, length:%d\n", (unsigned int)pllihead, lli_length);
    retval = dmac_buildllim2m(pllihead, source, dest, length, DMAC_TRANS_SIZE);
    dma_cache_clean((AARCHPTR)pllihead, ((AARCHPTR)pllihead) + lli_length);

    if (retval) {
        dma_err("build lli error...\n");
        return -1;
    }
    dmac_register_isr(chnn, dmac_channel_close);

    retval = dmac_start_llim2m(chnn, pllihead);
    if (retval) {
        return -1;
    }

    if (dmac_channelstart(chnn) != 0) {
        dma_err("start channel error...\n");
        return -1;
    }
    LOS_EventRead(&dmac_event[chnn], DMAC_EVENTT_TYPE, LOS_WAITMODE_OR + LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
    return retval;
}

/*
 * execute memory to memory dma transfer without LLI
 */
int dmac_m2m_transfer(unsigned int source,
                      unsigned int dest,
                      unsigned int length)
{
    unsigned int ulch = 0;
    unsigned int dma_size = 0;
    unsigned int dma_count = 0;
    unsigned int left_size;

    int ret;

    left_size = length;

    if (mem_check(source) | mem_check(dest)) {
        dma_err("Invalid dma addr psrc = 0x%x, pdest = 0x%x!\n",
                source, dest);
        return -EINVAL;
    }
    //if src addr or dst addr no aligned by 64bit
    //then copy by cpu.
    if ((source & (CACHE_ALIGNED_SIZE - 1)) || (dest & (CACHE_ALIGNED_SIZE - 1)) || \
            (length & (CACHE_ALIGNED_SIZE - 1))) {
        memcpy_s((void *)dest, length, (void *)source, length);
        dma_err("dma addr is not aligned by CACHE_ALIGNED_SIZE,coping by cpu !\n");
        return 0;
    }

    ulch = dmac_channel_allocate(NULL);
    if (DMAC_CHANNEL_INVALID == ulch) {
        return -1;
    }
    dma_cache_clean(source, source + length);
    dma_cache_inv(dest, dest + length);

    dma_size   = DMAC_TRANS_SIZE;
    while ((left_size >> 2) >= dma_size) {
        left_size -= (dma_size << 2);

        ret = dmac_start_m2m(ulch,
                             (unsigned int)(source + dma_count * (dma_size << 2)),
                             (unsigned int)(dest + dma_count * (dma_size << 2)),
                             dma_size);        //(dma_size << 2));
        if (ret) {
            dma_err("set dma mem  error...\n");
        }
        if (dmac_channelstart(ulch) != 0) {
            dma_err("start channel error...\n");
            return -1;
        }

        if (dmac_wait(ulch) != DMAC_CHN_SUCCESS) {
            dma_err("dma transfer error...\n");
            return -1;
        }

        dma_count++;
    }

    dmac_start_m2m(ulch,
                   (source + dma_count * (dma_size << 2)),
                   (dest + dma_count * (dma_size << 2)),
                   (left_size >> 2));              //(left_size << 2));

    if (dmac_channelstart(ulch) != 0) {
        return -1;
    }

    if (dmac_wait(ulch) != DMAC_CHN_SUCCESS) {
        return -1;
    }

    dmac_channelclose(ulch);
    return 0;
}

int dmac_m2p_transfer(unsigned int memaddr,
                      unsigned int uwperipheralid,
                      unsigned int length)
{
    unsigned int ulch, size = 0;
    unsigned int dma_count = 0, left_size = 0;
    unsigned int uwwidth;

    left_size = length;

    ulch = dmac_channel_allocate(NULL);
    if (DMAC_CHANNEL_INVALID == ulch) {
        return -1;
    }

    uwwidth = g_peripheral[uwperipheralid].transfer_width;

    if ((memaddr & (CACHE_ALIGNED_SIZE - 1))) {
        dma_err("dma addr is not aligned by CACHE_ALIGNED_SIZE!\n");
        dmac_channel_free(ulch);
        return -1;
    }
    dma_cache_clean(memaddr, memaddr + (((int)length + CACHE_ALIGNED_SIZE) & (~(CACHE_ALIGNED_SIZE - 1))));
    while ((left_size >> uwwidth) >= 0xffc) {
        size = 0xffc;
        left_size -= (size << uwwidth);

        if (dmac_start_m2p(ulch,
                           (unsigned int)(memaddr + dma_count * size),
                           uwperipheralid, (size << uwwidth), 0) < 0) {
            dmac_channel_free(ulch);
            return -1;
        }
        if (dmac_channelstart(ulch) != 0) {
            dma_err("[%d]: dma start channel failed\n", dma_count);
            dmac_channel_free(ulch);
            return -1;
        }
        if (dmac_wait(ulch) != DMAC_CHN_SUCCESS)
            if (dmac_channelstart(ulch) != 0) {
                dmac_channel_free(ulch);
                return -1;
            }
        if (dmac_wait(ulch) != DMAC_CHN_SUCCESS) {
            dma_err("[%d]: dma wait failed\n", dma_count);
            dmac_channel_free(ulch);
            return -1;
        }
        dma_count++;
    }

    dma_debug("memaddr=0x%x\n", (memaddr + dma_count * size));
    if (left_size > 0) {
        if (dmac_start_m2p(ulch,
                           (memaddr + dma_count * size),
                           uwperipheralid, left_size, 0) < 0) {
            dmac_channel_free(ulch);
            return -1;
        }
        if (dmac_channelstart(ulch) != 0) {
            dma_err("[%d]: dma start channel failed\n", dma_count);
            dmac_channel_free(ulch);
            return -1;
        }
    }
    return ulch;
}

/*
 * execute peripheral to memory dma transfer with lLI
 */
int dmac_llip2m_transfer(unsigned int memaddr,
                         unsigned int peripheralid,
                         unsigned int length,
                         unsigned int *channel)
{
    int retval = 0;
    int lli_length = 0;
    unsigned int chnn = 0;
    dmac_lli *pheadlli = NULL;

    if ((memaddr & (CACHE_ALIGNED_SIZE - 1)) ||
            (length & (CACHE_ALIGNED_SIZE - 1))) {
        dma_err("dma addr or length not aligned by CACHE_ALIGNED_SIZE!\n");
        return -1;
    }
    if ((peripheralid >= DMAC_MAX_PERIPHERALS) || (!channel)) {
        dma_err("peripheralid out or channel is null!\n");
        return -2;
    }
    chnn = dmac_channel_allocate(NULL);
    if (DMAC_CHANNEL_INVALID == chnn) {
        return -1;
    }

    lli_length = allocate_dmalli_space(&pheadlli, length);
    if (lli_length < 0) {
        return -1;
    }

    /*save head*/
    pheadlliList[chnn] = pheadlli;

    dmac_buildllip2m(pheadlli, peripheralid, memaddr, length, DMAC_TRANS_SIZE);

    dma_cache_clean((AARCHPTR)pheadlli, ((AARCHPTR)pheadlli) + lli_length);

    retval = dmac_start_llip2m(chnn, pheadlli, peripheralid);
    if (retval) {
        return -1;
    }

    if (dmac_channelstart(chnn) != 0) {
        dma_err("start channel error...\n");
        return -1;
    }
    *channel = chnn;
    return retval;
}

/*
 * execute memory to peripheral dma transfer without LLI
 */
int dmac_p2m_transfer(unsigned int memaddr,
                      unsigned int uwperipheralid,
                      unsigned int length)
{
    unsigned int ulch, dma_size = 0;
    unsigned int count = 0;
    unsigned int left_size;
    unsigned int uwwidth;

    left_size = length;

    ulch = dmac_channel_allocate(NULL);
    if (DMAC_CHANNEL_INVALID == ulch) {
        return -1;
    }

    uwwidth = g_peripheral[uwperipheralid].transfer_width;

    if ((memaddr & (CACHE_ALIGNED_SIZE - 1))) {
        dma_err("dma addr is not aligned by CACHE_ALIGNED_SIZE!\n");
        dmac_channel_free(ulch);
        return -1;
    }
    dma_cache_inv(memaddr, memaddr + (((int)length + CACHE_ALIGNED_SIZE) & (~(CACHE_ALIGNED_SIZE - 1))));
    while ((left_size >> uwwidth) >= 0xffc) {
        dma_size = 0xffc;
        left_size -= (dma_size << uwwidth);

        if (dmac_start_p2m(ulch,
                           (unsigned int)(memaddr + count * dma_size),
                           uwperipheralid, (dma_size << uwwidth), 0) < 0) {
            dmac_channel_free(ulch);
            return -1;
        }
        if (dmac_channelstart(ulch) != 0) {
            dmac_channel_free(ulch);
            return -1;
        }
        if (dmac_wait(ulch) != DMAC_CHN_SUCCESS) {
            dmac_channel_free(ulch);
            return -1;
        }
        count++;
    }
    if (left_size > 0) {
        if (dmac_start_p2m(ulch,
                           (unsigned int)(memaddr + count * dma_size),
                           uwperipheralid, left_size, 0) < 0) {
            dmac_channel_free(ulch);
            return -1;
        }
        if (dmac_channelstart(ulch) != 0) {
            dmac_channel_free(ulch);
            return -1;
        }
    }
    return ulch;
}


int do_dma_m2p(unsigned int memaddr,
               unsigned int peri_addr,
               unsigned int length)
{
    int retval = 0;
    int uwperipheralid;

    uwperipheralid = dmac_check_request(peri_addr, TX);
    if (uwperipheralid < 0) {
        dma_err("m2p:Invalid devaddr\n");
        return -1;
    }

    retval = dmac_m2p_transfer(memaddr, uwperipheralid, length);
    if (retval == -1) {
        dma_err("m2p:trans err\n");
        return -1;
    }

    return retval;
}

int do_dma_p2m(unsigned int memaddr, unsigned int peri_addr, unsigned int length)
{
    int retval = -1;
    int uwperipheralid;

    uwperipheralid = dmac_check_request(peri_addr, RX);
    if (uwperipheralid < 0) {
        dma_err("p2m:Invalid devaddr.\n");
        return -1;
    }

    retval = dmac_p2m_transfer(memaddr, uwperipheralid, length);
    if (retval == -1) {
        dma_err("p2m:trans err\n");
        return -1;
    }

    return retval;
}

void dmac_suspend(unsigned int channel)
{
    /*disable dmac*/
    if (DMAC_NOT_FINISHED == g_chan_status[channel]) {

        plliList_bak[channel] = (dmac_lli_bak *)zalloc(sizeof(dmac_lli_bak));
        if (NULL == plliList_bak[channel]) {
            dma_err("DMA suspend request failed\n");
            return;
        }
        hidmac_readw(DMAC_CxSRCADDR(channel), plliList_bak[channel]->src_addr);
        hidmac_readw(DMAC_CxDESTADDR(channel), plliList_bak[channel]->dst_addr);
        hidmac_readw(DMAC_CxLLI(channel), plliList_bak[channel]->next_lli);
        hidmac_readw(DMAC_CxCONTROL(channel), plliList_bak[channel]->lli_transfer_ctrl);
        hidmac_readw(DMAC_CxCONFIG(channel), plliList_bak[channel]->lli_transfer_config);

        hidmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE);
    }
}

void dmac_resume(unsigned int channel)
{
    hidmac_clk_en();
    hidmac_unreset();

    hidmac_writew(DMAC_CONFIG, DMAC_CONFIG_VAL);
    if (DMAC_NOT_FINISHED == g_chan_status[channel]) {
        if (NULL != plliList_bak[channel]) {
            hidmac_writew(DMAC_CxSRCADDR(channel), plliList_bak[channel]->src_addr);
            hidmac_writew(DMAC_CxDESTADDR(channel), plliList_bak[channel]->dst_addr);
            hidmac_writew(DMAC_CxLLI(channel), plliList_bak[channel]->next_lli);
            hidmac_writew(DMAC_CxCONTROL(channel), plliList_bak[channel]->lli_transfer_ctrl);
            hidmac_writew(DMAC_CxCONFIG(channel), plliList_bak[channel]->lli_transfer_config);

            free(plliList_bak[channel]);
            plliList_bak[channel] = NULL;
        }
    }
}

int dmac_init(struct platform_device *dev)
{
    unsigned int i = 0;
    unsigned int tempvalue = 0;
    struct resource *res = NULL;
    unsigned int irq;

    res = platform_get_resource(dev, IORESOURCE_MEM, 0);
    if (NULL == res) {
        dma_err("platform_get_resource IORESOURCE_MEM fail!\n");
        return -1;
    }
    reg_dmac_base_va = platform_ioremap_resource(res);
    irq = platform_get_irq(dev, 0);
    if (LOS_NOK == irq) {
        dma_err("platform_get_irq fail!\n");
        return -1;
    }

    hidmac_clk_en();

    hidmac_unreset();

    hidmac_readw(DMAC_CONFIG, tempvalue);
    if (tempvalue == 0) {
        hidmac_writew(DMAC_CONFIG, DMAC_CONFIG_VAL);
        hidmac_writew(DMAC_INTTCCLEAR, 0xFF);
        hidmac_writew(DMAC_INTERRCLR, 0xFF);
        for (i = 0; i < DMAC_MAX_CHANNELS; i++) {
            hidmac_writew(DMAC_CxCONFIG(i), DMAC_CxDISABLE);
            g_chan_status[i] = DMAC_CHN_VACANCY;
            pheadlliList[i] = NULL;
            isr_function[i] = NULL;
            (void)LOS_EventInit(&dmac_event[i]);
        }
    }

    if (request_irq(irq, &dmac_isr, (unsigned long)NULL, "hi_dma", NULL)) {
        dma_err("DMA Irq %d request failed\n", NUM_HAL_INTERRUPT_DMAC);
        return -1;
    }

    return 0;
}

int dmac_remove(struct platform_device *dev)
{
    int i;

    for (i = 0; i < DMAC_MAX_CHANNELS; i++) {
        g_chan_status[i] = DMAC_CHN_VACANCY;
    }

    return 0;
}

static int hi_dmac_suspend(struct device *dev)
{
    struct platform_device *pdev = to_platform_device(dev);

    return 0;
}

static int hi_dmac_resume(struct device *dev)
{
    struct platform_device *pdev = to_platform_device(dev);

    return 0;
}

static const struct dev_pm_op hi_dmac_dev_pm_ops = {
    .suspend = hi_dmac_suspend,
    .resume = hi_dmac_resume,
};


/** */
static int hi_dmac_platform_probe(struct platform_device *dev)
{
    return dmac_init(dev);
}

static int hi_dmac_platform_remove(struct platform_device *dev)
{
    return dmac_remove(dev);
}

static struct platform_driver hi_dmac_driver = {
    .probe      = hi_dmac_platform_probe,
    .remove     = hi_dmac_platform_remove,
    .driver     = {
        .name   = DMAC_DRIVER_NAME,
        .pm = &hi_dmac_dev_pm_ops,
    },
};

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif

struct resource hidmac_resources[] = {
    {
        .start  = DMAC_REG_BASE,
        .end    = DMAC_REG_BASE + 0x2000 - 1,
        .flags  = IORESOURCE_MEM,
    },
    {
        .start  = NUM_HAL_INTERRUPT_DMAC,
        .end    = NUM_HAL_INTERRUPT_DMAC,
        .flags  = IORESOURCE_IRQ,
    }
};

struct platform_device hi_dmac_device = {
    .name = DMAC_DRIVER_NAME,
    .id   = 0,
    .num_resources = ARRAY_SIZE(hidmac_resources),
    .resource = hidmac_resources,
};

int /*__init*/ hi_dmac_platform_init(void)
{
    int ret = 0;

    ret = platform_device_register(&hi_dmac_device);
    if (ret) {
        dma_err("register platform device failed!");
        goto _error_register_device;
    }

    ret = platform_driver_register(&hi_dmac_driver);
    if (ret) {
        dma_err("register platform driver failed!");
        goto _error_register_driver;
    }

    return ret;

_error_register_driver:
    platform_device_unregister(&hi_dmac_device);

_error_register_device:

    return -1;
}

void /*__exit*/ hi_dmac_platform_exit(void)
{
    platform_driver_unregister(&hi_dmac_driver);

    platform_device_unregister(&hi_dmac_device);
}

module_init(hi_dmac_platform_init);
module_exit(hi_dmac_platform_exit);

/*end of file hi_dmac.c*/

//////////////////////////////
///just used for debug
//////////////////////////////
int hi_dmac_init(void)
{
    return hi_dmac_platform_init();
}

int hi_dmac_remove(void)
{
    hi_dmac_platform_exit();

    return 0;
}

